package com.donnees.mysql;


import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.swing.JOptionPane;

import com.metier.logique.*;

import com.donnees.dao.AbstractDAO;


/**
 * @name AbstractDAO
 * @description Objet DAO abstrait parent de tous les DAO, quel que soit la fabrique (MySQL ici uniquement, mais ça marcherait avec d'autres).
 * @method public - abstract - MSG - find - Méthode générique traitant l'objet MSG reçu en paramètre et créant la requête SQL en fonction du MSG.
 * @method public - abstract - MSG - findAll - Méthode récupérant tous les résultats sans filtrage.
 * @method public - abstract - MSG - delete - Méthode générique traitant l'objet MSG reçu en paramètre et créant la requête SQL en fonction du MSG.
 * @method public - abstract - MSG - update - Méthode générique traitant l'objet MSG reçu en paramètre et créant la requête SQL en fonction du MSG.
 * @method public - abstract - MSG - insert - Méthode générique traitant l'objet MSG reçu en paramètre et créant la requête SQL en fonction du MSG.
 * @method protected - MSG - getNombreLignesByResulSet - Retourne le nombre de lignes d'un ResultSet.
 * 
 * @author Ambroise
 * @param <T>
 * @since 07/03/2012
 */
public abstract class MySqlDAO<T> extends AbstractDAO<T>{

	protected String sql;
	protected BigDecimal lastInsertedId;
	protected ResultSet resultats;
	protected PreparedStatement requetePreparee;
	
	/**
	 * @name find
	 * @description Méthode générique traitant l'objet MSG reçu en paramètre et créant la requête SQL en fonction du MSG.
	 * @param MSG oMSG
	 * @return MSG
	 */
	/**
	 * @name find
	 * @description Méthode générique traitant l'objet MSG reçu en paramètre et créant la requête SQL en fonction du MSG.
	 * 
	 * @param {@link MSG} oMSG<br />
	 * <b>select</b> - String - Obligatoire - Doit contenir tous les champs à récupérer séparés par des virgules ainsi que les éventuels count et leur alias. <br />
	 * 		  	- Ex: "SELECT ID_PERSONNE, NOM, PRENOM, COUNT(ID_PERSONNE) AS COUNT_TOTAL"<br />
	 * 
	 * <b>from</b> - String - Obligatoire - Doit contenir la totalité de la chaîne FROM de la requête SQL. (Les tables ainsi que les jointures entre les tables.)<br />
	 * 			- Ex: "FROM personne, types" | "FROM personne LEFT OUTER JOIN types ON personne.TYPE_PERSONNE = types.ID_TYPES"<br />
	 * 
	 * <b>parametresInconnus</b> - MSG - Optionnel - Doit contenir autant de valeurs qu'il y a de paramètres dans la requête préparée.<br />
	 * 			- Ex: 	<br /><br /><code>
	  					Personne oPersonne = new Personne();
						
						Object[] parametresInconnus = new Object[2];
						parametresInconnus[0] = 1;
						parametresInconnus[1] = 14;
						
						MSG oMSG3 = new MSG();
						oMSG3.setData("parametresInconnus", parametresInconnus);
						oMSG3.setData("select", "SELECT ID_PERSONNE, NOM, PRENOM");
						oMSG3.setData("from", "FROM personne");
						oMSG3.setData("where", "WHERE ID_PERSONNE BETWEEN ? AND ?");
						oMSG3.setData("orderBy", "ORDER BY NOM, PRENOM, ID_PERSONNE");
						
						ResultSet result = oPersonne.get(oMSG3);
						while(result.next()){
							System.out.println("ID:" + result.getInt("ID_PERSONNE") + ", Nom: " +result.getString("NOM") + ", Prénom: " + result.getString("PRENOM"));
						}
	 * </code><br /><br />
	 * <b>where</b> - String - Optionnel - Doit contenir la clause WHERE de la requête SQL. (Pour limiter les résultats, réaliser des jointures, etc...)<br />
	 * 			- Ex: "WHERE ID_PERSONNE = 5" | WHERE NOM LIKE '%bouh%'<br />
	 * 
	 * 		Utiliser les requêtes préparées:	- Ex: "WHERE ID_PERSONNE = ?" | WHERE ID_PERSONNE IN (?) | WHERE NOM LIKE '?'<br />
	 * 
	 * <b>groupBy</b> - String - Optionnel - Doit contenir la clause GROUP BY de la requête SQL. (En général utilisé lors d'un COUNT)<br />
	 * 			- Ex: "GROUP BY NOM" | "GROUP BY NOM, PRENOM"<br />
	 * 
	 * <b>orderBy</b> - String - Optionnel - Doit contenir la clause ORDER BY de la requête SQL. (select ASC/DESC concaténés entres eux)<br />
	 * 		  	- Ex: "ORDER BY NOM, PRENOM, DATE_NAISSANCE DESC"<br />
 	 *			
	 * @return {@link ResultSet} this.resultats - Résultats de la méthode SELECT.<br />
	 */
	public MSG find(MSG oMSG){
		this.sql = "";
		
		if(oMSG.getData("select") != null && oMSG.getData("from") != null){
			// Création de la requête SQL.
			this.sql +=  oMSG.getData("select") + " " + oMSG.getData("from") + " ";
			
			if(oMSG.getData("where") != null){
				this.sql +=  oMSG.getData("where") + " ";
			}
			
			if(oMSG.getData("groupBy") != null){
				this.sql +=  oMSG.getData("groupBy") + " ";				
			}
			
			if(oMSG.getData("orderBy") != null){
				this.sql +=  oMSG.getData("orderBy") + " ";				
			}
			
			this.sql += ";";
			
			
			try{
				Connection oConnection = MySqlDAOFactory.getConnection();
			
				this.requetePreparee = oConnection.prepareStatement(
					this.sql,
					Statement.RETURN_GENERATED_KEYS// On souhaite récupérer les clés, que ce soit le dernier ID inséré ou bien le nombre de lignes modifiées/supprimées.
				);
				
				// Configuration des paramètres d'une éventuelle requête paramétrée.
				if(oMSG.getData("parametresInconnus") != null){
					Object[] parametresInconnus = (Object[]) oMSG.getData("parametresInconnus");
					
					// On appelle la méthode de génération des paramètres.
					generateParameters(parametresInconnus);
				}
				
				// On affiche la requête si le projet est en mode debuguage.
				if(ConfigurationProjet.MODE_DEBUG){
					System.out.println(this.requetePreparee);
				}
				
				oMSG.setData("params", this.requetePreparee);
				
				// On exécute la requête.
				this.resultats = (ResultSet) requetePreparee.executeQuery();
				
			}catch (Exception e) {
				JOptionPane.showMessageDialog(null, "Erreur inconnue lors de l'exécution de la méthode get().\n\n<html><body><u>Message d'erreur</u>:<body></html>\n" + e.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE);
			}
		}else{
			JOptionPane.showMessageDialog(null, "Erreur lors de l'exécution de la méthode get(). Les arguments 'select' et 'from' sont obligatoires.", "Arguments manquants", JOptionPane.ERROR_MESSAGE);
		}
		
		oMSG.setData("donnees", this.resultats);
		
		return oMSG;
	}
	
	/**
	 * @name findAll
	 * @description Méthode récupérant tous les résultats sans filtrage.
	 * @return MSG
	 */
	public MSG findAll(){return null;};
	
	/**
	 * @name delete
	 * @description Méthode générique traitant l'objet MSG reçu en paramètre et créant la requête SQL en fonction du MSG.
	 * @param MSG oMSG
	 * @return MSG
	 */
	public MSG delete(MSG oMSG){return null;};
	
	/**
	 * @name update
	 * @description Méthode générique traitant l'objet MSG reçu en paramètre et créant la requête SQL en fonction du MSG.
	 * @param MSG oMSG
	 * @return MSG
	 */
	public MSG update(MSG oMSG){return null;};
	
	/**
	 * @name insert
	 * @description Méthode générique traitant l'objet MSG reçu en paramètre et créant la requête SQL en fonction du MSG.
	 * @param MSG oMSG
	 * @return MSG
	 */
	public MSG insert(MSG oMSG){return null;};
	
	/**
	 * @name getNombreLignesByResulSet
	 * @description Retourne le nombre de lignes d'un ResultSet.
	 * @since 28/02/2012
	 */
	protected int getNombreLignesByResulSet(ResultSet resultSet){
		try {
			resultSet.last();
			int nombreLignes = resultSet.getRow();
			resultSet.beforeFirst();
			
			return nombreLignes;
		} catch (SQLException e) {
			if(ConfigurationProjet.MODE_DEBUG){
				System.out.println("Exception SQL dans getNombreLigne:\n\n"+e.getMessage());
				e.printStackTrace();
			}
			return -1;
		}
	}
	
	/**
	 * @name generateParameters
	 * @description: Modifie la requetePreparee courante en lui rajoutant des attributs.
	 * @param Object[] parametresInconnus - Tableau d'Objects - Contient un tableau d'objets indexés numériquement.<br />
	 * 			Ex:<br /><br /><code>
				    Object[] parametresInconnus = new Object[13];
					parametresInconnus[0] = "Dhenain";
					parametresInconnus[1] = "Ambroise";
					try {
						parametresInconnus[2] = GestionnaireDate.getJavaSqlDateByDate(new SimpleDateFormat("yyyy/MM/dd").parse("1990/04/06"));
					} catch (ParseException e) {
						parametresInconnus[2] = new java.sql.Date(0L);
					}
					parametresInconnus[3] = "";
					parametresInconnus[4] = "";
					parametresInconnus[5] = "";
					parametresInconnus[6] = "";
					parametresInconnus[7] = "";
					parametresInconnus[8] = "";
					parametresInconnus[9] = "";
					parametresInconnus[10] = "";
					parametresInconnus[11] = 0;
					parametresInconnus[12] = 0;
	 * </code>
	 */
	protected void generateParameters(Object[] parametresInconnus){
		try{
			int i = 1;// Compteur
			// Pour chaque case, en fonction du type de la variable on prépare la ligne différemment.
			for (Object value : parametresInconnus) {
				if(value.getClass() == String.class){
					this.requetePreparee.setString(i, (String)value);
				}else if(value.getClass() == Integer.class){
					this.requetePreparee.setInt(i, (Integer)value);
				}else if(value.getClass() == Boolean.class){
					this.requetePreparee.setBoolean(i, (Boolean)value);
				}else if(value.getClass() == java.sql.Date.class){
					this.requetePreparee.setDate(i, (java.sql.Date)value);
				}else if(value.getClass() == BigDecimal.class){
					this.requetePreparee.setBigDecimal(i, (BigDecimal)value);
				}else if(value.getClass() == Float.class){
					this.requetePreparee.setFloat(i, (Float)value);
				}else if(value.getClass() == Double.class){
					this.requetePreparee.setDouble(i, (Double)value);
				}else if(value.getClass() == Short.class){
					this.requetePreparee.setShort(i, (Short)value);
				}else if(value.getClass() == Byte.class){
					this.requetePreparee.setByte(i, (Byte)value);
				}else if(value.getClass() == Long.class){
					this.requetePreparee.setLong(i, (Long)value);
				}else if(value.getClass() == Character.class){// On convertit un Character en String - Non testé !
					this.requetePreparee.setString(i, (String)value);
				}else{
					JOptionPane.showMessageDialog(null, "Alerte, cas non prévu lors de la méthode generateParameters(), le type fourni n'est pas géré lors de la génération de la requête préparée !", "Alerte !", JOptionPane.WARNING_MESSAGE);
				}
				
				i++;
			}
		}catch (Exception e) {
			JOptionPane.showMessageDialog(null, "Une erreur est survenue lors de l'exécution de la méthode generateParameters().\n\n<html><body><u>Message d'erreur</u>:<body></html>\n" + e.getMessage(), "Erreur inconnue !", JOptionPane.ERROR_MESSAGE);
		}
	}
}
